﻿using ArtMath.Animation.Converter;
using ArtMath.Attribute;
using ArtMath.Component.Point;
using ArtMath.Core.Data;
using ArtMath.Core.Helper;
using ArtMath.Data;
using ArtMath.CommonType;
using ArtMath.Resource;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Windows.Forms;

namespace ArtMath.Animation
{
    public enum Direction
    {
        Forward = 1,
        Backward = -1,
    }
    [Serializable]
    public abstract class AnimationBase : IRecoverableDelete, ISerializable
    {
        PadStringX lineSpeed, acceleration;
        double lineSpeedPerRate, duration;
        protected double timePerElapse;
        int frameRate;
        Direction direction;
        Random rand;
        [Browsable(false)]
        public ObjectState ObjectState { get; private set; } = ObjectState.Normal;
        [NonSerialized]
        ListViewItem animationsManagerItem;
        [Browsable(false)]
        public ListViewItem AnimationsManagerItem { get { return animationsManagerItem; } }
        [NonSerialized]
        ListViewItem recycleItem;
        [Browsable(false)]
        public ListViewItem RecycleItem { get { return recycleItem; } }
        public event Action<AnimationBase> Recovered;
        public AnimationBase(PointBase pt, string linespeed, int frmRate = 25, Direction dir = Direction.Forward)
        {
            frameRate = frmRate;
            MotionPoint = pt;
            animationsManagerItem = new ListViewItem(new string[] { MotionPoint.Name, "0s" }) { Tag = this };
            LineSpeed = new PadStringX(Pad, linespeed);
            MotionDirection = dir;
        }
        void refreshLineSpeedPerRate()
        {
            lineSpeedPerRate = LineSpeed.Value / FrameRate * (int)MotionDirection;
        }
        [Browsable(false)]
        public SketchPad Pad
        {
            get { return MotionPoint.Pad; }
        }
        [MultilingualCategory("Structure"), MultilingualDisplayName("MovingPoint")]
        public PointBase MotionPoint { get; set; }
        [MultilingualCategory("Structure"), MultilingualDisplayName("LinearVelocity")]
        public PadStringX LineSpeed
        {
            get { return lineSpeed; }
            set
            {
                if (lineSpeed is object)
                {
                    lineSpeed.ParametersValueChanged -= LineSpeed_ParametersValueChanged;
                    lineSpeed.ExpressionSet -= LineSpeed_ExpressionSet;
                }
                lineSpeed = value;
                if (lineSpeed is object)
                {
                    lineSpeed.ParametersValueChanged += LineSpeed_ParametersValueChanged;
                    lineSpeed.ExpressionSet += LineSpeed_ExpressionSet;
                }
            }
        }
        [MultilingualCategory("Structure"), MultilingualDisplayName("Acceleration")]
        public PadStringX Acceleration
        {
            get { return acceleration; }
            set
            {
                if (acceleration is object)
                {
                    acceleration.ParametersValueChanged -= Acceleration_ParametersValueChanged;
                    acceleration.ExpressionSet -= Acceleration_ExpressionSet;
                }
                acceleration = value;
                if (acceleration is object)
                {
                    acceleration.ParametersValueChanged += Acceleration_ParametersValueChanged;
                    acceleration.ExpressionSet += Acceleration_ExpressionSet;
                }
            }
        }

        void Acceleration_ExpressionSet(StringX sender, ExpressionSetEventArgs e)
        {

        }

        void Acceleration_ParametersValueChanged(DocStringX sender, ParametersValueChangedEventArgs e)
        {

        }

        [MultilingualCategory("Structure"), MultilingualDisplayName("FrameRate")]
        public virtual int FrameRate
        {
            get { return frameRate; }
            set
            {
                frameRate = value;
                refreshLineSpeedPerRate();
            }
        }
        [MultilingualCategory("Structure"), MultilingualDisplayName("MotionDirection"), TypeConverter(typeof(DirectionConverter)), DefaultValue(typeof(Direction), "Forward")]
        public Direction MotionDirection
        {
            get { return direction; }
            set
            {
                direction = value;
                refreshLineSpeedPerRate();
            }
        }
        [Browsable(false)]
        public abstract bool IsRunning { get; }
        [MultilingualCategory("Parameter"), MultilingualDisplayName("Duration"), ReadOnly(true)]
        public double Duration
        {
            get { return duration; }
            set
            {
                duration = value;
                AnimationsManagerItem.ListView.CrossThreadInvoke(() => { AnimationsManagerItem.SubItems[1].Text = duration.ToString("0.00") + "s"; });
                //string str = duration.ToString("0.00") + "s";
                //if (LinkedListViewItem.ListView.InvokeRequired)
                //{
                //    Action<string> act = (string x) => { LinkedListViewItem.SubItems[1].Text = x; };
                //    LinkedListViewItem.ListView.Invoke(act, str);
                //}
                //else
                //{
                //    LinkedListViewItem.SubItems[1].Text = str;
                //}
            }
        }
        [Browsable(false)]
        public string Name
        {
            get { return MotionPoint.OfTranslator("Animation"); }
        }
        [Browsable(false)]
        public bool CanRecover
        {
            get { return ObjectState == ObjectState.Deleted && MotionPoint.ObjectState == ObjectState.Normal; }
        }

        void LineSpeed_ParametersValueChanged(DocStringX sender, ParametersValueChangedEventArgs e)
        {
            if (lineSpeed.Value >= 0)
            {
                refreshLineSpeedPerRate();
            }
        }
        void LineSpeed_ExpressionSet(StringX sender, ExpressionSetEventArgs e)
        {
            if (lineSpeed.Value >= 0)
            {
                refreshLineSpeedPerRate();
            }
            else
            {
                Pad.ShowMessage(Resources.JoinLocalizedString("LinearVelocity", "MustNonNegative"), MessageType.Exclamation);
                lineSpeed.SetExpression(e.OldExpression, false);
            }
        }
        public virtual void Run()
        {
            Pad.MoveObject_DoMouseDown(MotionPoint);
        }
        public virtual void Stop()
        {
            Pad.MoveObject_DoMouseUp(MotionPoint);
        }
        public void Reverse()
        {
            MotionDirection = MotionDirection == Direction.Forward ? Direction.Backward : Direction.Forward;
        }
        protected void Move()
        {
            if (MotionPoint == null || lineSpeedPerRate == 0) return;
            PointD tmp;
            MotionPoint.BufferedMoveSpace = MotionPoint.GetMoveSpace();
            bool reverse = false;
            if (MotionPoint.BufferedMoveSpace.IsUniversal)
            {
                if (rand == null) rand = new Random();
                tmp = MotionPoint.ToPointD().Offset(rand.NextDouble() * Core.Helper.Angle.RadRound, lineSpeedPerRate);
            }
            else
            {
                tmp = MotionPoint.BufferedMoveSpace.SlideDestination(MotionPoint.ToPointD(), lineSpeedPerRate, out reverse);
            }
            if (Pad.Coordinate != null) tmp = Pad.Coordinate.XYToPxy(tmp);
            Pad.MoveObject_DoMouseMove(MotionPoint, tmp);
            if (reverse)
            {
                Reverse();
            }
        }

        public void Delete(bool recoverable = false)
        {
            Stop();
            if (recoverable)
            {
                if (RecycleItem == null)
                {
                    recycleItem = new ListViewItem(new string[] { MotionPoint.Name, Resources.Translate("Animation") }) { Tag = this };
                }
                UIInteraction.RecycleBin.Delete(this);
                ObjectState = ObjectState.Deleted;
            }
            else
            {
                if (RecycleItem != null)
                {
                    RecycleItem.Remove();
                }
                Pad.Animations.Remove(this);
                ObjectState = ObjectState.Disposed;
            }
            if (Pad.IsSelectedPad)
            {
                AnimationsManagerItem.Remove();
            }
        }

        public void Recover()
        {
            RecycleItem.Remove();
            Recovered?.Invoke(this);
            ObjectState = ObjectState.Normal;
        }

        public virtual void BeforeSerializing(Dictionary<string, object> data)
        {

        }

        public virtual void AfterDeserialized(Dictionary<string, object> data)
        {
            animationsManagerItem = new ListViewItem(new string[] { MotionPoint.Name, Duration.ToString("0.00") + "s" }) { Tag = this };
            if (ObjectState == ObjectState.Deleted)
            {
                recycleItem = new ListViewItem(new string[] { MotionPoint.Name, Resources.Translate("Animation") }) { Tag = this };
            }
        }

        public virtual void ChangePoint(PointBase oldPoint, PointBase newPoint)
        {
            if (ReferenceEquals(MotionPoint, oldPoint))
            {
                MotionPoint = newPoint;
            }
        }
    }
}
